﻿Imports ACompany.OrderProcessing.Infrastructure
Imports System.Collections.ObjectModel

Public Class Order
    Inherits BusinessBase(Of Order)
    Implements IAggregateRoot
    Implements IEntity

    Private _Customer As Customer
    Private _DespatchAddress As Address
    Private _OrderDate As Date
    Private _shippedDate As Nullable(Of Date)
    Private _status As OrderStatus
    Private _items As IList(Of Item)
    Private _version As Integer

    Public Sub New()
        ' Default Constructor required for NHibernate
        _status = OrderStatus.placed
        _OrderDate = Now
        _items = New List(Of Item)
    End Sub

    Public Property OrderDate() As Date
        Get
            Return _OrderDate
        End Get
        Set(ByVal value As Date)
            _OrderDate = value
        End Set
    End Property

    Public Property Customer() As Customer
        Get
            Return _Customer
        End Get
        Set(ByVal value As Customer)
            _Customer = value
        End Set
    End Property

    Public Property DespatchAddress() As Address
        Get
            Return _DespatchAddress
        End Get
        Set(ByVal value As Address)
            _DespatchAddress = value
        End Set
    End Property

    Public ReadOnly Property Status() As OrderStatus
        Get
            Return _status
        End Get
    End Property

    Public ReadOnly Property ShippedDate() As Nullable(Of Date)
        Get
            Return _shippedDate
        End Get
    End Property

    Public ReadOnly Property Items() As ReadOnlyCollection(Of Item)
        Get
            Return New ReadOnlyCollection(Of Item)(_Items)
        End Get
    End Property

#Region "Version for concurrency checking"

    Public ReadOnly Property Version() As Integer
        Get
            Return _version
        End Get
    End Property

#End Region

#Region "Methods"

    Public Sub Remove(ByVal Item As Item)

        _items.Remove(Item)
    End Sub

    Public Sub Add(ByVal Product As Product)

        If Not OrderContains(Product) Then
            Dim newItem As New Item
            newItem.Order = Me
            newItem.Product = Product
            newItem.Qty = 1
            newItem.Price = Product.Price

            _items.Add(newItem)
        Else
            IncreaseQtyOfItemWithMatching(Product)
        End If

    End Sub

    Private Sub IncreaseQtyOfItemWithMatching(ByVal Product As Product)
        For Each Item As Item In _items
            If Item.Product.Equals(Product) Then
                Item.Qty += 1
            End If
        Next
    End Sub

    Private Function OrderContains(ByVal Product As Product) As Boolean

        Dim bolFound As Boolean = False

        For Each Item As Item In _Items
            If Item.Product.Equals(Product) Then
                bolFound = True
            End If
        Next

        Return bolFound
    End Function

    Public Function CanCancel() As Boolean

        ' TODO: Should refactor to the State Pattern
        If Me.Status = OrderStatus.placed Then
            Return True
        Else
            Return False
        End If

    End Function

    Public Function CanProcess() As Boolean

        ' TODO: Should refactor to the State Pattern
        If Me.Status = OrderStatus.placed Then
            ' Check to see if we have enough stock 
            ' to process this order
            Return HaveEnoughStockToProcess()
        Else
            Return False
        End If

    End Function

    Public Sub Cancel()
        If CanCancel() Then
            _status = OrderStatus.cancelled
        Else
            Throw New InvalidOperationException("This order cannot be cancelled")
        End If
    End Sub

    Public Sub Process()
        If CanProcess() Then
            _status = OrderStatus.shipped
            _shippedDate = Now
        Else
            Throw New InvalidOperationException("This order cannot be processed")
        End If
    End Sub

    Public Function HaveEnoughStockToProcess() As Boolean

        Dim enoughStock As Boolean = True

        For Each i As Item In Me.Items
            If i.Qty > i.Product.Stock Then
                enoughStock = False
            End If
        Next

        Return enoughStock

    End Function

#End Region

    Protected Overrides Sub validate()

        If Items Is Nothing Then
            Me.BrokenRules.Add(New BrokenRule("Items", "An order must have at least one order item"))
        Else
            If Items.Count = 0 Then
                Me.BrokenRules.Add(New BrokenRule("Items", "An order must have at least one order item"))
            Else
                For Each Item As Item In Items
                    For Each br As BrokenRule In Item.GetBrokenRules
                        Me.BrokenRules.Add(br)
                    Next
                Next
            End If
        End If

        If Customer Is Nothing Then
            Me.BrokenRules.Add(New BrokenRule("Customer", "An order must have a customer"))
        End If

        If DespatchAddress Is Nothing Then
            Me.BrokenRules.Add(New BrokenRule("DespatchAddress", "An order must have a Despatch Address"))
        Else
            'For Each br As String In Me.DespatchAddress.GetBrokenRules
            '    Me.BrokenRules.Add(br)
            'Next
        End If

        If Not [Enum].IsDefined(GetType(OrderStatus), Me.Status) Then
            Me.BrokenRules.Add(New BrokenRule("OrderStatus", "An order must have a valid status"))
        End If

        ' Can't have a shipped date if not processed

        ' Must have a shipped date if processed


    End Sub
End Class
